/**
  ******************************************************************************
  * @file    libusb_adb.c
  * @author  Newtons_Prism
  * @version V1.0.0
  * @date    19-April-2022
  * @brief   USB ADB Library
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT 2022 Newtons_Prism</center></h2>
  *
  * Licensed under GNU General Public License v3.0, (the "License");
  * You may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
  *
  *        https://www.gnu.org/licenses/gpl-3.0
  *
  * Unless required by applicable law or agreed to in writing, software 
  * distributed under the License is distributed on an "AS IS" BASIS, 
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *
  ******************************************************************************
  */
 

#include "libusb_adb.h"

/*
* 函数名         : is_adb_interface
* 函数功能		 : 判断是否是ADB接口
* 输入           : usb_class：USB类；usb_subclass：USB子类；usb_protocol：USB协议
* 输出         	 : 成功：0；失败：-1
*/
int is_adb_interface(int usb_class, int usb_subclass, int usb_protocol)
{
	if (usb_class == ADB_CLASS && usb_subclass == ADB_SUBCLASS && usb_protocol == ADB_PROTOCOL) {
		return 1;
	}
	return 0;
}

/*
* 函数名         : get_apacket
* 函数功能		 : 数据包指针动态分配内存
* 输入           : 无
* 输出         	 : 数据包指针
*/
apacket* get_apacket(void)
{
	apacket* p = (apacket*)malloc(sizeof(apacket));
	memset(p, 0, sizeof(apacket));
	return p;
}

/*
* 函数名         : put_apacket
* 函数功能		 : 释放数据包指针
* 输入           : p：数据包指针；
* 输出         	 : 无
*/
void put_apacket(apacket* p)
{
	free(p);
}

/*
* 函数名         : usb_read
* 函数功能		 : USB读取数据
* 输入           : data：数据指针； len：数据量
* 输出         	 : 成功：0；失败：-1
*/
int usb_read(adb_handle* handle, void* data, int len)
{
	int ret;
	if (NULL != handle) {
		int xfer = (len > MAX_PAYLOAD) ? MAX_PAYLOAD : len;
		ret = libusb_bulk_transfer(handle->dev_handle,
			handle->adb_read_pipe,
			(uint8_t*)data,
			xfer,
			NULL,
			0);
		if (ret == 0) {
			return 0;
		}
	}
	return -1;
}

/*
* 函数名         : check_header
* 函数功能		 : 检验数据头有效性
* 输入           : p：数据包指针
* 输出         	 : 成功：0；失败：-1
*/
int check_header(apacket* p)
{
	if (p->msg.magic != (p->msg.command ^ 0xffffffff)) {
		return -1;
	}
	if (p->msg.data_length > MAX_PAYLOAD) {
		return -1;
	}
	return 0;
}

/*
* 函数名         : check_data
* 函数功能		 : 检验数据有效性
* 输入           : p：数据包指针
* 输出         	 : 成功：0；失败：-1
*/
int check_data(apacket* p)
{
	uint8_t* x;
	uint16_t count, sum;
	count = p->msg.data_length;
	x = p->data;
	for (sum = 0; count > 0; count--) {
		sum += *x++;
	}
	if (sum != p->msg.data_check) {
		return -1;
	}
	else {
		return 0;
	}
}

/*
* 函数名         : remote_read
* 函数功能		 : 读取数据包
* 输入           : handle：ADB结构体；p：数据包指针
* 输出         	 : 成功：0；失败：-1
*/
int remote_read(adb_handle* handle, apacket* p)
{
	if (usb_read(handle, &p->msg, sizeof(amessage))) {  //接收数据头
		return -1;
	}
	printf("remote_read p->cmd:%04x\n", p->msg.command);
	if (check_header(p)) {								//检验数据头
		return -1;
	}
	if (p->msg.data_length) {
		if (usb_read(handle, p->data, p->msg.data_length)) {  //接收数据
			return -1;
		}
		printf("remote_read p->data:%s\n", p->data);
		if (check_data(p)) {							//检验数据
			return -1;
		}
	}
	return 0;
}

/*
* 函数名         : usb_write
* 函数功能		 : USB发送数据
* 输入           : handle：ADB结构体；data：数据指针； len：数据长度
* 输出         	 : 成功：0；失败：-1
*/
int usb_write(adb_handle* handle, const void* data, int len) {
	int ret;
	if (NULL != handle) {
		ret = libusb_bulk_transfer(handle->dev_handle,
			handle->adb_write_pipe,
			(uint8_t*)data,
			len,
			NULL,
			0);
		if (ret == 0) {
			if (handle->zero_mask && (len & handle->zero_mask) == 0) {  //确保数据发送完
				libusb_bulk_transfer(handle->dev_handle,
					handle->adb_write_pipe,
					(uint8_t*)data,
					0,
					NULL,
					0);
			}
			return 0;
		}
	}
	return -1;
}

/*
* 函数名         : remote_write
* 函数功能		 : 发送数据头和数据
* 输入           : handle：ADB结构体；p：数据包指针
* 输出         	 : 成功：0；失败：-1
*/
int remote_write(adb_handle* handle, apacket* p)
{
	uint16_t len = p->msg.data_length;
	if (usb_write(handle, &p->msg, sizeof(amessage))) {  //发送数据头
		return -1;
	}
	printf("remote_write p->cmd:%04x\n", p->msg.command);
	if (p->msg.data_length == 0) {
		return 0;
	}
	if (usb_write(handle, p->data, len)) {	//发送数据
		return -1;
	}
	printf("remote_write p->data:%s\n", p->data);
	return 0;
}

/*
* 函数名         : send_packet_remote
* 函数功能		 : 发送数据包
* 输入           : handle：ADB结构体；p：数据包指针
* 输出         	 : 无
*/
void send_packet_remote(adb_handle* handle, apacket* p)
{
	uint8_t* x;
	uint16_t count, sum;
	p->msg.magic = p->msg.command ^ 0xffffffff; //校验标识符
	count = p->msg.data_length;
	x = p->data;
	for (sum = 0; count > 0; count--) {		 //校验数据
		sum += *x++;
	}
	p->msg.data_check = sum;
	remote_write(handle, p);
}

/*
* 函数名         : send_auth_remote
* 函数功能		 : 发送auth key，向设备申请USB调试权限
* 输入           : handle：ADB结构体；key：auth key（任意字符串即可）
* 输出         	 : 成功：0；失败：-1
*/
int send_auth_remote(adb_handle* handle, const char* key)
{
	int r;
	apacket* p = get_apacket();
	uint16_t len = (uint16_t)strlen(key) + 1;
	if (len > (MAX_PAYLOAD - 1)) {
		printf("destination oversized\n");
		return -1;
	}
	do {
		p->msg.command = A_AUTH;
		p->msg.arg0 = ADB_AUTH_RSAPUBLICKEY;
		p->msg.arg1 = 0;
		p->msg.data_length = len;
		strcpy((char*)p->data, key);
		send_packet_remote(handle, p);
		do {
			memset(p, 0, sizeof(apacket));
			r = remote_read(handle, p);
		} while (!(r == 0));
	} while (!(p->msg.command == A_CNXN));
	put_apacket(p);
	return 0;
}

/*
* 函数名         : send_cmd_remote
* 函数功能		 : 发送shell命令行
* 输入           : handle：ADB结构体；cmd：命令行字符串
* 输出         	 : 成功：0；失败：-1
*/
int send_cmd_remote(adb_handle* handle, const char* cmd)
{
	apacket* p = get_apacket();
	uint16_t len = (uint16_t)strlen(cmd) + 1;
	if (len > (MAX_PAYLOAD - 1)) {
		printf("destination oversized\n");
		return -1;
	}
	p->msg.command = A_OPEN;
	p->msg.arg0 = A_VERSION;
	p->msg.arg1 = 0;
	p->msg.data_length = len;
	strcpy((char*)p->data, cmd);
	send_packet_remote(handle, p);
	do {
		memset(p, 0, sizeof(apacket));
		remote_read(handle, p);
	} while (!(p->msg.command == A_OKAY));
	put_apacket(p);
	return 0;
}

/*
* 函数名         : match_with_endpoint
* 函数功能		 : 匹配端点
* 输入           : handle：ADB结构体；interface：ADB接口号
* 输出         	 : 成功：1；失败：0
*/
int match_with_endpoint(adb_handle* handle, const struct libusb_interface_descriptor* interface)
{
	uint8_t i;
	uint8_t ret = 0;
	for (i = 0; i < interface->bNumEndpoints; i++) {
		if (interface->endpoint[i].bEndpointAddress & 0x80) {
			ret |= 1;
			handle->adb_read_pipe = interface->endpoint[i].bEndpointAddress; //读端点地址
		}
		else {
			ret |= 2;
			handle->adb_write_pipe = interface->endpoint[i].bEndpointAddress; //写端点地址
		}
	}
	if (ret == 3) {
		return 1;
	}
	else {
		return 0;
	}
}

/*
* 函数名         : match_with_interface
* 函数功能		 : 匹配ADB接口
* 输入           : handle：ADB结构体
* 输出         	 : 无
*/
void match_with_interface(adb_handle* handle)
{
	libusb_device** devs;
	libusb_device* dev;
	ssize_t cnt;
	uint8_t num = 0;
	int r;
	cnt = libusb_get_device_list(NULL, &devs); //获取设备列表
	if (cnt < 0) {
		printf("failed to libusb_get_device_list\n");
		return;
	}
	while ((dev = devs[num++]) != NULL) {			//遍历设备列表
		uint8_t i, j;
		struct libusb_device_descriptor desc;
		struct libusb_config_descriptor* config;
		struct libusb_interface_descriptor interface;
		r = libusb_get_device_descriptor(dev, &desc);	//获取设备描述符
		if (r < 0) {
			printf("failed to get device descriptor\n");
			return;
		}
		r = libusb_get_config_descriptor(dev, 0, &config);	//获取配置描述符
		if (r < 0) {
			printf("failed to get config descriptor\n");
			return;
		}
		for (i = 0; i < config->bNumInterfaces; i++) {		//遍历设备接口，找到ADB接口；一个设备可有多个不同功能的接口
			for (j = 0; j < config->interface[i].num_altsetting; j++) {
				if (is_adb_interface(config->interface[i].altsetting[j].bInterfaceClass,
					config->interface[i].altsetting[j].bInterfaceSubClass,
					config->interface[i].altsetting[j].bInterfaceProtocol)) {
					interface = config->interface[i].altsetting[j];		//判断端点数是否满足，一个收，一个发
					if (2 != interface.bNumEndpoints) {
						return;
					}
					if (match_with_endpoint(handle, &interface)) {		//匹配读写端点
						handle->zero_mask = interface.endpoint[0].wMaxPacketSize - 1;
						handle->dev_handle = libusb_open_device_with_vid_pid(NULL, desc.idVendor, desc.idProduct);	//打开设备
						printf("idVendor:%04x, idProduct:%04x\n", desc.idVendor, desc.idProduct);
						if (handle->dev_handle != NULL) {
							r = libusb_claim_interface(handle->dev_handle, interface.bInterfaceNumber); //注册ADB接口
							if (r < 0) {
								printf("*** libusb_claim_interface failed! \n");
								return;
							}
							handle->state = INIT_DONE;
							return;
						}
					}
				}
			}
		}
	}
}


